//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.eternizedlab.lunarcalendar;

import java.util.Calendar;
import java.util.Locale;

/**
 * Lunar data for China and Korea.
 */
public class LunarData {
  // private static final String TAG = LunarData.class.getSimpleName();

  public static final int LUNAR_INFO_FIRST_YEAR = 1900;
  public static final int LUNAR_INFO_LAST_YEAR = 2049;

  private static final int[] CHINA_LUNAR_INFO = new int[] { 0x04bd8, 0x04ae0,
      0x0a570, 0x054d5, 0x0d260,//
      0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,//
      0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255,//
      0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,//
      0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40,//
      0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,//
      0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0,//
      0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,//
      0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4,//
      0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,//
      0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0,//
      0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0,//
      0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570,//
      0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,//
      0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4,//
      0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,//
      0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a,//
      0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,//
      0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50,//
      0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,//
      0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552,//
      0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,//
      0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9,//
      0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,//
      0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60,//
      0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,//
      0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0,//
      0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,//
      0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577,//
      0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,//
  };

  private static final int[] KOREA_LUNAR_INFO = new int[] { 0x04bd8, 0x04ae0,
      0x0a570, 0x054d5, 0x0d260,//
      0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,//
      0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x0da95,//
      0x0b550, 0x056a0, 0x0ada2, 0x095d0, 0x04bb7,//
      0x049b0, 0x0a4b0, 0x0b4b5, 0x06a90, 0x0ad40,//
      0x0bb54, 0x02b60, 0x095b0, 0x05372, 0x04970,//
      0x06566, 0x0e4a0, 0x0ea50, 0x16a95, 0x05b50,//
      0x02b60, 0x18ae3, 0x092e0, 0x1c8d7, 0x0c950,//
      0x0d4a0, 0x1d8a6, 0x0b690, 0x056d0, 0x125b4,//
      0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0d557,//
      0x0b4a0, 0x0b550, 0x15555, 0x04db0, 0x025b0,//
      0x18573, 0x052b0, 0x0a9b8, 0x06950, 0x06aa0,//
      0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570,//
      0x05270, 0x07263, 0x0d950, 0x06b57, 0x056a0,//
      0x09ad0, 0x04dd5, 0x04ae0, 0x0a4e0, 0x0d4d4,//
      0x0d250, 0x0d598, 0x0b540, 0x0d6a0, 0x195a6,//
      0x095b0, 0x049b0, 0x0a9b4, 0x0a4b0, 0x0b27a,//
      0x06a50, 0x06d40, 0x0b756, 0x02b60, 0x095b0,//
      0x04b75, 0x04970, 0x064b0, 0x074a3, 0x0ea50,//
      0x06d98, 0x05ad0, 0x02b60, 0x096e5, 0x092e0,//
      0x0c960, 0x0e954, 0x0d4a0, 0x0da50, 0x07552,//
      0x056c0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,//
      0x0a950, 0x0b4a0, 0x1b4a3, 0x0b550, 0x055d9,//
      0x04ba0, 0x0a5b0, 0x05575, 0x052b0, 0x0a950,//
      0x0b954, 0x06aa0, 0x0ad50, 0x06b52, 0x04b60,//
      0x0a6e6, 0x0a570, 0x05270, 0x06a65, 0x0d930,//
      0x05aa0, 0x0b6a3, 0x096d0, 0x04afb, 0x04ae0,//
      0x0a4d0, 0x1d0d6, 0x0d250, 0x0d520, 0x0dd45,//
      0x0b6a0, 0x096d0, 0x055b2, 0x049b0, 0x0a577,//
      0x0a4b0, 0x0b250, 0x1b255, 0x06d40, 0x0ada0,//
  };

  public static final int TERM_FIRST_YEAR = 1901;
  public static final int TERM_LAST_YEAR = 2050;

  private static final char[] TERMS_INFO = new char[] { 0x96, 0xB4, 0x96, 0xA6,
      0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1901
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1902
      0x96, 0xA5, 0x87, 0x96, 0x87, 0x87, 0x79, 0x69, 0x69, 0x69, 0x78, 0x78, // 1903
      0x86, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x78, 0x87, // 1904
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1905
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1906
      0x96, 0xA5, 0x87, 0x96, 0x87, 0x87, 0x79, 0x69, 0x69, 0x69, 0x78, 0x78, // 1907
      0x86, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1908
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1909
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1910
      0x96, 0xA5, 0x87, 0x96, 0x87, 0x87, 0x79, 0x69, 0x69, 0x69, 0x78, 0x78, // 1911
      0x86, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1912
      0x95, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1913
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1914
      0x96, 0xA5, 0x97, 0x96, 0x97, 0x87, 0x79, 0x79, 0x69, 0x69, 0x78, 0x78, // 1915
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1916
      0x95, 0xB4, 0x96, 0xA6, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x87, // 1917
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x77, // 1918
      0x96, 0xA5, 0x97, 0x96, 0x97, 0x87, 0x79, 0x79, 0x69, 0x69, 0x78, 0x78, // 1919
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1920
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x87, // 1921
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x77, // 1922
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x69, 0x69, 0x78, 0x78, // 1923
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1924
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x87, // 1925
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1926
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1927
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1928
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1929
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1930
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x87, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1931
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1932
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1933
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1934
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1935
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1936
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1937
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1938
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1939
      0x96, 0xA5, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1940
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1941
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1942
      0x96, 0xA4, 0x96, 0x96, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1943
      0x96, 0xA5, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1944
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1945
      0x95, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, // 1946
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1947
      0x96, 0xA5, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 1948
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x79, 0x78, 0x79, 0x77, 0x87, // 1949
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, // 1950
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x79, 0x79, 0x79, 0x69, 0x78, 0x78, // 1951
      0x96, 0xA5, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 1952
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1953
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x68, 0x78, 0x87, // 1954
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1955
      0x96, 0xA5, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 1956
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1957
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1958
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1959
      0x96, 0xA4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1960
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1961
      0x96, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1962
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1963
      0x96, 0xA4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1964
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1965
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1966
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1967
      0x96, 0xA4, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1968
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1969
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1970
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x79, 0x69, 0x78, 0x77, // 1971
      0x96, 0xA4, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1972
      0xA5, 0xB5, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1973
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1974
      0x96, 0xB4, 0x96, 0xA6, 0x97, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, // 1975
      0x96, 0xA4, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x89, 0x88, 0x78, 0x87, 0x87, // 1976
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 1977
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x78, 0x87, // 1978
      0x96, 0xB4, 0x96, 0xA6, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, // 1979
      0x96, 0xA4, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1980
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x77, 0x87, // 1981
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1982
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x78, 0x79, 0x78, 0x69, 0x78, 0x77, // 1983
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, // 1984
      0xA5, 0xB4, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 1985
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1986
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x79, 0x78, 0x69, 0x78, 0x87, // 1987
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 1988
      0xA5, 0xB4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1989
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 1990
      0x95, 0xB4, 0x96, 0xA5, 0x86, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1991
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 1992
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1993
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1994
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x76, 0x78, 0x69, 0x78, 0x87, // 1995
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 1996
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 1997
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 1998
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 1999
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 2000
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2001
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 2002
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 2003
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 2004
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2005
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2006
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x69, 0x78, 0x87, // 2007
      0x96, 0xB4, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, // 2008
      0xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2009
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2010
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x78, 0x87, // 2011
      0x96, 0xB4, 0xA5, 0xB5, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, // 2012
      0xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, // 2013
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2014
      0x95, 0xB4, 0x96, 0xA5, 0x96, 0x97, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 2015
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, // 2016
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, // 2017
      0xA5, 0xB4, 0xA6, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2018
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 2019
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x86, // 2020
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 2021
      0xA5, 0xB4, 0xA5, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2022
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x79, 0x77, 0x87, // 2023
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, // 2024
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 2025
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2026
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 2027
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, // 2028
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 2029
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2030
      0xA5, 0xB4, 0x96, 0xA5, 0x96, 0x96, 0x88, 0x78, 0x78, 0x78, 0x87, 0x87, // 2031
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, // 2032
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x86, // 2033
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x78, 0x88, 0x78, 0x87, 0x87, // 2034
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2035
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, // 2036
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x86, // 2037
      0xA5, 0xB3, 0xA5, 0xA5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2038
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2039
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x96, // 2040
      0xA5, 0xC3, 0xA5, 0xB5, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, // 2041
      0xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x88, 0x88, 0x88, 0x78, 0x87, 0x87, // 2042
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2043
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x88, 0x87, 0x96, // 2044
      0xA5, 0xC3, 0xA5, 0xB4, 0xA5, 0xA6, 0x87, 0x88, 0x87, 0x78, 0x87, 0x86, // 2045
      0xA5, 0xB3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x88, 0x78, 0x87, 0x87, // 2046
      0xA5, 0xB4, 0x96, 0xA5, 0xA6, 0x96, 0x88, 0x88, 0x78, 0x78, 0x87, 0x87, // 2047
      0x95, 0xB4, 0xA5, 0xB4, 0xA5, 0xA5, 0x97, 0x87, 0x87, 0x88, 0x86, 0x96, // 2048
      0xA4, 0xC3, 0xA5, 0xA5, 0xA5, 0xA6, 0x97, 0x87, 0x87, 0x78, 0x87, 0x86, // 2049
      0xA5, 0xC3, 0xA5, 0xB5, 0xA6, 0xA6, 0x87, 0x88, 0x78, 0x78, 0x87, 0x87 // 2050
  };

  private final int[] lunarInfo;

  public LunarData() {
    if (Locale.KOREA.equals(Locale.getDefault())) {
      lunarInfo = KOREA_LUNAR_INFO;
    } else {
      lunarInfo = CHINA_LUNAR_INFO;
    }
  }

  /**
   * Get the term index of a particular day.
   *
   * @return Term index ranging [0..23]. -1 if the day is not a term day.
   */
  public int getTermIdx(Calendar calendar) {
    int year = calendar.get(Calendar.YEAR);
    if (year < TERM_FIRST_YEAR || year > TERM_LAST_YEAR) {
      return -1;
    }
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    int beginIdx = (year - TERM_FIRST_YEAR) * 12 + month;
    int firstTermInMonth = 15 - ((TERMS_INFO[beginIdx] >> 4) & 0xf);
    int secondTermInMonth = (TERMS_INFO[beginIdx] & 0xf) + 15;
    if (firstTermInMonth == day) {
      return month * 2;
    } else if (secondTermInMonth == day) {
      return month * 2 + 1;
    } else {
      return -1;
    }
  }

  /**
   * Get the index of leap month.
   *
   * @param lunarYear
   *          year index measured in solar calendar
   * @return Leap month index ranging [1..12]. 0 if there is no leap month.
   */
  public int getLeapMonthIdx(int lunarYear) {
    if (lunarYear < LUNAR_INFO_FIRST_YEAR || lunarYear > LUNAR_INFO_LAST_YEAR) {
      return 0;
    }
    return lunarInfo[lunarYear - LUNAR_INFO_FIRST_YEAR] & 0xf;
  }

  /**
   * Get the number of days in a particular month.
   *
   * @param lunarYear
   *          lunar year
   * @param month
   *          Months [1..12]. 0 for leap month.
   * @return Number of days. Returns 29. if month is 0 and there is actually no
   *         leap month.
   */
  public int getDaysInMonth(int lunarYear, int month) {
    if (lunarYear < LUNAR_INFO_FIRST_YEAR || lunarYear > LUNAR_INFO_LAST_YEAR
        || month < 0 || month > 12) {
      return 0;
    }
    return (lunarInfo[lunarYear - LUNAR_INFO_FIRST_YEAR] & (0x10000 >> month)) > 0 ? 30
        : 29;
  }

  /**
   * Get the number of days in a particular year.
   *
   * @return number of days
   */
  public int getDaysInYear(int lunarYear) {
    if (lunarYear < LUNAR_INFO_FIRST_YEAR || lunarYear > LUNAR_INFO_LAST_YEAR) {
      return 0;
    }
    int sum = 0;
    for (int i = getLeapMonthIdx(lunarYear) > 0 ? 0 : 1; i < 13; ++i) {
      sum += (lunarInfo[lunarYear - LUNAR_INFO_FIRST_YEAR] & (0x10000 >> i)) > 0 ? 30
          : 29;
    }
    return sum;
  }

  private static final int[] holidayMonth = new int[] { 1, 1, 5, 7, 8, 9, 11 };
  private static final int[] holidayDay = new int[] { 1, 15, 5, 7, 15, 9, 8 };

  /**
   * Get the index of holiday.
   *
   * @return Holiday idx. -1 if the day is not a holiday.
   */
  public int getHolidayIdx(int lunarYear, int month, boolean isLeapMonth,
      int day) {
    if (!isValidDate(lunarYear, month, isLeapMonth, day)) {
      return -1;
    }
    if (isLeapMonth) {
      // Currently no holidays are accounted for leap months.
      return -1;
    }
    int count = holidayDay.length;
    for (int i = 0; i < count; ++i) {
      if (month == holidayMonth[i] && day == holidayDay[i]) {
        return i;
      }
    }
    // For holiday ChuXi
    if (month == 12 && day == getDaysInMonth(lunarYear, month)) {
      return 7/* ChuXi */;
    }
    return -1;
  }

  public int getAnimalsYearIdx(int lunarYear) {
    return (lunarYear - 4) % 12;
  }

  public int getGanIdx(int lunarYear) {
    return (lunarYear - 1900 + 36) % 10;
  }

  public int getZhiIdx(int lunarYear) {
    return (lunarYear - 1900 + 36) % 12;
  }

  public int getAncientHourIdx(Calendar calendar) {
    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    return hour >= 23 || hour < 1 ? 0 : (hour - 1) / 2 + 1;
  }

  private boolean isValidDate(int lunarYear, int month, boolean isLeapMonth,
      int day) {
    return !(lunarYear < LUNAR_INFO_FIRST_YEAR
        || lunarYear > LUNAR_INFO_LAST_YEAR || month < 1 || month > 12
        || day < 1 || day > 30 || isLeapMonth
        && getLeapMonthIdx(lunarYear) != month || getDaysInMonth(lunarYear,
        isLeapMonth ? 0 : month) < day);
  }
}
